package org.puppet.game.element;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.puppet.game.Controlable;
import org.puppet.game.World;
import org.puppet.game.base.PuppetUtils;
import org.puppet.game.control.PuppetDrawController;
import org.puppet.game.control.PuppetUpdateController;
import org.puppet.game.element.PuppetInfo.AnimInfo;

import android.content.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;

public abstract class Puppet implements Controlable {

	protected AnimArrayList anims;
	protected int animNum = 0;
	protected int animLength = 0;

	protected boolean isFaceLeft = false;
	protected boolean isVisible;

	public final static Paint DEFAULT_PAINT = new Paint();

	public final static int collisionPX = 15;

	public float x, y;

	public float vX, vY;

	public float acceX, acceY;

	public float topVX, topVY;

	protected int animationNo;
	
	protected Rect coreRect=new Rect();
	
	protected PuppetUpdateController updateController;
	protected PuppetDrawController drawController;

	public Puppet() {
		Resources res = World.current.getResources();

		PuppetInfo animInfo = getAnimationInfo(res);
		AnimInfo[] animis = animInfo.getAnims();
		Bitmap[] bitmaps = createBitmaps(animInfo);
		
		animLength = animis.length;
		animNum = animLength << 1;
		anims = new AnimArrayList(animNum);		
		List<Animation> ranims = new ArrayList<Animation>(animLength);
		
		for (int i = 0; i < animLength; i++) {
			AnimInfo ani = animis[i];
			long[] durations = ani.getDurations();
			int start = ani.getStart();
			
			Animation la = new Animation(this);
			Animation ra = new Animation(this);

			for (int j = 0, end = durations.length; j < end; j++) {
				long l = durations[j];
				Bitmap lf = bitmaps[start++];
				Bitmap rf = PuppetUtils.mirrorBitmap(lf);
				la.addFrame(lf, l);
				ra.addFrame(rf, l);
			}
			la.setAllFrameCoreXY(ani.getCoreX(), ani.getCoreY());
			ra.setAllFrameCoreXY(ani.getCoreX(), ani.getCoreY());

			anims.add(la);
			ranims.add(ra);
		}
		anims.addAll(ranims);
		
		updateController = new PuppetUpdateController(this);
		drawController = new PuppetDrawController(this);
	}

	protected Bitmap[] createBitmaps(PuppetInfo animInfo) {
		List<Bitmap> bs = new ArrayList<Bitmap>();
		
		Bitmap bitmap = animInfo.getBitmap();
		int totleWidth = bitmap.width();
		int totleHeight = bitmap.height();

		int fwidth = animInfo.getFrameWidth();
		int fheight = animInfo.getFrameHeight();
		
		int cwidth = 0, cheight = 0;
		while(cheight + fheight <= totleHeight){
			if(cwidth + fwidth <= totleWidth){
				bs.add(PuppetUtils.createBitmap(bitmap, cwidth, cheight, fwidth, fheight));				
				cwidth += fwidth;
			}else{
				cwidth = 0;
				cheight += fheight;
			}
		}
		
		return bs.toArray(new Bitmap[0]);
	}

	protected abstract PuppetInfo getAnimationInfo(Resources res);

	public void live() {
		World.current.getCurrentView().addPuppet(this);
		World.current.reg(updateController);
		World.current.getCurrentView().getDirector().reg(drawController);
	}
	
	public void die(){
		World.current.getDirector().unreg(updateController);
		World.current.getCurrentView().getDirector().unreg(drawController);		
		World.current.getCurrentView().deletePuppet(this);
	}
	
	public void onAnimationEnd(int actionNo){
		
	}

	public void setAnimations(AnimArrayList anims) {
		this.anims = anims;
	}

	public void addAnimation(Animation anim) {
		anims.add(anim);
		animNum++;
	}

	public float[] getDeltaPosition(long elapsedTime) {
		vX = vX + acceX * elapsedTime;
		vY = vY + acceY * elapsedTime;
		return new float[]{vX * elapsedTime , vY * elapsedTime };
	}
	
	public void update(long elapsedTime) {
		updateAnim(elapsedTime);
	}

	public void updateAnim(long elapsedTime) {
		for (int i = 0; i < animNum; i++) {
			anims.get(i).update(elapsedTime);
		}
	}

	public void drawMe(Canvas canvas) {
		canvas.drawBitmap(getCurrentAnim().getImage(), getLeft(), getTop(), null);
	}

	public void changeAnimation(int animationNo) {
		this.animationNo = isFaceLeft ? animationNo : animationNo + animLength;
		getCurrentAnim().reset();
	}

	public Bitmap getImage() {
		return getCurrentAnim().getImage();
	}

	public float getLeft() {
		return x - getCurrentAnim().coreX;
	}

	public float getTop() {
		return y - getCurrentAnim().coreY;
	}

	public float getRight() {
		return getLeft() + getWidth();
	}

	public float getBottom() {
		return getTop() + getHeight();
	}

	public void setX(float x) {
		this.x = x;
	}

	public void setY(float y) {
		this.y = y;
	}

	public void setLeft(float left) {
		x = left + getCurrentAnim().coreX;
	}

	public void setTop(float top) {
		y = top + getCurrentAnim().coreY;
	}

	public int getWidth() {
		return getCurrentAnim().width;
	}

	public int getHeight() {
		return getCurrentAnim().height;
	}

	public Animation getCurrentAnim() {
		return anims.get(animationNo);
	}

	public int getAnimationNo() {
		return animationNo;
	}

	public void setAnimationNo(int animationNo) {
		this.animationNo = animationNo;
	}

	public boolean collision(Puppet other){
		return getCoreRect().intersect(other.getCoreRect());
	}
	
	public int collisionDir(Puppet other) {

		boolean cLeft, cRight, cTop, cButtom;

		float myLeft = getLeft(), myTop = getTop(), myRight = getRight(), myBottom = getBottom();
		int myWidth = getWidth(), myHeight = getHeight();

		float itsLeft = other.getLeft(), itsTop = other.getTop(), itsRight = other.getRight(), itsBottom = other.getBottom();
		int itsWidth = other.getWidth(), itsHeight = other.getHeight();

		cLeft = (myLeft <= itsRight) && (itsRight - myLeft <= myWidth + itsWidth - collisionPX);
		cRight = (myRight >= itsLeft) && (myRight - itsLeft <= myWidth + itsWidth - collisionPX);
		cTop = (myTop <= itsBottom) && (itsBottom - myTop <= myHeight + itsHeight - collisionPX);
		cButtom = (myBottom >= itsTop) && (myTop - itsBottom <= myHeight + itsHeight - collisionPX);

		if (cLeft && cTop) return 1;
		if (cLeft && cButtom) return 2;
		if (cRight && cTop) return 3;
		if (cRight && cButtom) return 4;
		
		if (cTop) return 5;
		if (cRight) return 6;
		if (cButtom) return 7;
		if (cLeft ) return 8;

		return 0;
	}

	public Rect getCoreRect() {
		coreRect.set(getCurrentAnim().getCoreRect());
		coreRect.offsetTo((int)getLeft(), (int)getTop());
		return coreRect;
	}
	
	public static class AnimArrayList {

		private List<Animation> as;

		public AnimArrayList(int capacity) {
			as = new ArrayList<Animation>(capacity);
		}

		public Animation get(int animationNo) {
			return as.get(animationNo);
		}

		public void add(Animation object) {
			as.add(object);
			object.setActionNo(as.size()-1);
		}

		public void addAll(Collection<Animation> ans) {
			for (Animation an : ans) {
				add(an);
			}
		}		
		
	}
}